import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { TWEEN } from 'three/examples/jsm/libs/tween.module.min'
import { Water } from 'three/examples/jsm/objects/Water2'
import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer'
import { Sky } from 'three/examples/jsm/objects/Sky'

import { Tool } from '../Tool/Tool'
let { DUTU_CONFIG } = require('../config')
let { GLB_PATH, OBJ_PATH, FBX_PATH, IMGES_PATH } = DUTU_CONFIG

export class Three extends Tool {
  constructor(object) {
    super(object)
  }
  /**
   * 加载外部导入的帧动画，并播放
   * @param {*} config.mesh 执行动画主体
   * @param {*} config.animations 帧动画执行过程，这里是多个动画数组
   * @param {*} config.index 默认全部播放即index=-1，否则传入需要播放的动画下标
   */
  addAni (config) {
    let { mesh, animations, index = -1 } = config
    let mixer = new THREE.AnimationMixer(mesh);
    var clock = new THREE.Clock();
    if (index === -1) {
      for (let i = 0; i < animations.length; i++) {
        mixer.clipAction(animations[i]).play();
        let ani = () => {
          requestAnimationFrame(ani)
          var delta = clock.getDelta();
          if (mixer != null) {
            mixer.update(delta);
          }
          mixer.update(clock.getDelta());
        }
        ani()
      }
    } else {
      mixer.clipAction(animations[index]).play();
      let ani = () => {
        requestAnimationFrame(ani)
        var delta = clock.getDelta();
        if (mixer != null) {
          mixer.update(delta);
        }
        mixer.update(clock.getDelta());
      }
      ani()
    }
  }
  /**addHalo
   * 增加光环
   * @param {*} config 
   */
  addHalo (config = {}) {
    let {
      haloFileName, repeatX = 1, repeatY = 1,
      speedX = 0, speedY = 0, offset = {
        x: 0, y: 0, z: 0
      }, position = {
        x: 0, y: 0, z: 0
      }, group, geometry
    } = config
    let halo = new THREE.TextureLoader().load(IMGES_PATH + haloFileName) // 光环
    halo.wrapS = halo.wrapT = THREE.RepeatWrapping;
    halo.repeat.set(repeatX, repeatY)
    let haloMaterial = new THREE.MeshBasicMaterial({
      map: halo,
      side: THREE.FrontSide,
      transparent: true,
      opacity: 1
    })
    let haloMesh = new THREE.Mesh(geometry, haloMaterial);
    haloMesh.position.x = position.x + offset.x
    haloMesh.position.y = position.y + offset.y
    haloMesh.position.z = position.z + offset.z
    group.add(haloMesh)
    if (speedY || speedX) {
      let animate = () => {
        if (halo) {
          halo.offset.y += speedY
          halo.offset.x += speedX
        }
        requestAnimationFrame(animate)
      }
      animate()
    }
  }
  /**
   * 加入电子围栏（通过管道加载）
   */
  addElectronicFenceFormTube (config = {}) {
    let { visible = false, groupName = '',
      radius = 8, tension = 0, radialSegments = 8,
      attsList = [], skin = 'line_red.png'
      , repeatX = 1, repeatY = 1, speedX = 0, speedY = 0,
      haloFileName = '',
      pointsList = []
    } = config
    let group = new THREE.Group()
    group.visible = visible
    group.name = groupName
    this.otherGroup.add(group)
    let list2 = attsList.length > 0 ? attsList : pointsList
    for (let i = 0; i < list2.length; i++) {
      let attList = list2.length > 0 ? this.att2Points(attsList[i]) : pointsList
      let curve = new THREE.CatmullRomCurve3(attList, true, 'catmullrom', tension)
      let tubeGeometry = new THREE.TubeGeometry(curve, attList.length * 2, radius, radialSegments, true)
      let texture = new THREE.TextureLoader().load(IMGES_PATH + skin)
      texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
      texture.repeat.set(repeatX, repeatY)
      texture.offset.y = 0.3
      let material = new THREE.MeshBasicMaterial({
        map: texture,
        side: THREE.DoubleSide,
        transparent: true,
        opacity: 0.7
      })
      let mesh = new THREE.Mesh(tubeGeometry, material);
      mesh.position.y = 50
      group.add(mesh)
      if (haloFileName) {
        this.addHalo({
          haloFileName, speedX, speedY, group, geometry: tubeGeometry
        })
      }
    }
  }
  /**
   * 加入电子围栏
   * @param object.height 电子围栏高度
   * @param object.positionList 多个电子围栏坐标集合，数组内的item为Xyz格式
   * @param object.topColor 电子围栏颜色1，形成渐变色
   * @param object.bottomColor 电子围栏颜色2，形成渐变色
   * @param object.opacity 电子围栏透明度
   */
  addElectronicFenceFormBuf (config) {
    let { visible = false, groupName = '',
      attsList = [], repeatX = 1, repeatY = 1, speedX = 0, speedY = 0,
      topColor = 'rgba(123,255,255,0.1)',
      bottomColor = 'rgba(123,255,255,1)',
      color1List = [], pointsList = [],
      haloFileName = 'images/line9-1.png',
      height = 100, opacity = 1
    } = config
    let colorList1RGBA = [], colorList2RGBA = [], floars1RGBA = [], floars2RGBA = [], color2List = []
    if (color1List.length > 0) {
      color2List = color1List.map(item => item.split('0.1').join('0.7'))
      colorList1RGBA = color1List.map(item => item.substring(5, item.length - 1).split(','))
      colorList2RGBA = color2List.map(item => item.substring(5, item.length - 1).split(','))
      floars1RGBA = colorList1RGBA.map(item => item.map((ele, index) => {
        if (index < 3) {
          return Number((Number(ele) / 255).toFixed(1))
        } else {
          return Number(ele)
        }
      }))
      floars2RGBA = colorList2RGBA.map(item => item.map((ele, index) => {
        if (index < 3) {
          return Number((Number(ele) / 255).toFixed(1))
        } else {
          return Number(ele)
        }
      }))
    }
    let color1RGBA = topColor.substring(5, topColor.length - 1).split(',')
    let color2RGBA = bottomColor.substring(5, bottomColor.length - 1).split(',')
    let floar1RGBA = color1RGBA.map((item, index) => {
      if (index < 3) {
        return Number((Number(item) / 255).toFixed(1))
      } else {
        return Number(item)
      }
    })
    let floar2RGBA = color2RGBA.map((item, index) => {
      if (index < 3) {
        return Number((item / 255).toFixed(1))
      } else {
        return Number(item)
      }
    })
    let group = new THREE.Group()
    group.name = groupName
    group.visible = visible
    this.otherGroup.add(group)
    let list2 = attsList.length > 0 ? attsList : pointsList
    for (let j = 0; j < list2.length; j++) {
      const attList = attsList.length > 0 ? this.att2Points(list2[j]) : list2[j];
      let attributeList = []
      let uvList = []
      let colors = [];
      for (let i = 0; i < attList.length; i++) {
        const position = attList[i];
        const nextposition = attList[i === attList.length - 1 ? 0 : i + 1];
        attributeList.push(position.x, position.y + height, position.z)
        attributeList.push(nextposition.x, nextposition.y + height, nextposition.z)
        attributeList.push(position.x, position.y, position.z)
        attributeList.push(position.x, position.y, position.z)
        attributeList.push(nextposition.x, nextposition.y + height, nextposition.z)
        attributeList.push(nextposition.x, nextposition.y, nextposition.z)
        uvList = uvList.concat([1, 1, 0, 1, 1, 0])
        uvList = uvList.concat([1, 0, 0, 1, 0, 0])
        if (color1List.length > 0) {
          colors = colors.concat(floars1RGBA[j]).concat(floars1RGBA[j]).concat(floars2RGBA[j]).concat(floars2RGBA[j]).concat(floars1RGBA[j]).concat(floars2RGBA[j])
        } else {
          colors = colors.concat(floar1RGBA).concat(floar1RGBA).concat(floar2RGBA).concat(floar2RGBA).concat(floar1RGBA).concat(floar2RGBA)
        }
      }
      let geometry = new THREE.BufferGeometry();
      geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(attributeList), 3));
      geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvList), 2));
      geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 4));
      let material = new THREE.MeshBasicMaterial({
        // depthTest: false,
        // depthWrite: false,
        vertexColors: true,
        side: THREE.DoubleSide,
        transparent: true,
        opacity
      });
      let mesh = new THREE.Mesh(geometry, material)
      group.add(mesh)
      if (haloFileName) {
        this.addHalo({
          haloFileName, speedX, speedY, group, geometry, repeatX,
          repeatY
        })
      }
    }
  }
  /** addCurve3
   * 加入曲线
   * @param name 曲线在场景内的名称
   * @param height 曲线高度
   * @param startPoint 曲线开始点坐标
   * @param endPoint 曲线结束点坐标
   * @param skin 曲线皮肤
   * @param color 曲线颜色
   * @param beforeAdd 曲线模型被加入到场景之前触发，传入false则不加入场景，返回模型对象
   */
  addCurve3 (config) {
    let { name = '', height = '', startPoint, endPoint, skin = 'line_red.png', color, beforeAdd = () => { return true } } = config
    let middlePoint = {
      x: ((endPoint.x - startPoint.x) / 2) + startPoint.x,
      y: ((endPoint.y - startPoint.y) / 2) + startPoint.y + height,
      z: ((endPoint.z - startPoint.z) / 2) + startPoint.z,
    }
    const curve = new THREE.QuadraticBezierCurve3(
      new THREE.Vector3(startPoint.x, startPoint.y, startPoint.z),
      new THREE.Vector3(middlePoint.x, middlePoint.y, middlePoint.z),
      new THREE.Vector3(endPoint.x, endPoint.y, endPoint.z),
    );
    const points = curve.getPoints(50);
    this.addTube({
      name, points, skin, color, repeatX: 2, speedX: -0.01, repeatY: 1,
      beforeAdd: (mesh) => {
        beforeAdd(mesh)
        return false
      }
    })
  }
  /** 
   * 增加一个基础mesh
   * @param position <object> mesh定位
   * @param lng <number> mesh的经度（传入的是经纬度则需要转换）
   * @param lat <number> mesh的纬度（传入的是经纬度则需要转换）
   * @param sideLength <number> mesh边长
   */
  addMesh (object, getObject) {
    let position, lng, lat, sideLength, color
    if (object) {
      position = object.position || { x: 0, y: 0, z: 0 }
      lng = object.lng || -1
      lat = object.lat || -1
      sideLength = object.sideLength || 100
      color = object.color || 0xff0000
    } else {
      position = { x: 0, y: 0, z: 0 }
      lng = -1
      lat = -1
      sideLength = 100
      color = 0xff0000
    }
    // 没有传入经纬度则直接使用position
    let xyz
    if (lng === -1) {
      xyz = new THREE.Vector3(position.x, position.y, position.z)
    } else {
      xyz = this.lngLatToXyz({ lng, lat })
    }
    let box = new THREE.BoxGeometry(sideLength, sideLength, sideLength)
    let material = new THREE.MeshLambertMaterial({ color })
    let mesh = new THREE.Mesh(box, material)
    mesh.position.set(xyz.x, xyz.y, xyz.z)
    this.scene.add(mesh)
    if (getObject) {
      getObject(mesh)
    }
  }
  /**
    * addTube 添加管道
    * @param config.points  Vector3点集合
    * @param config.attList  三维顶点数组
    * @param config.repeatX  X方向贴图复制数
    * @param config.speedX  X方向UV动画速度
    * @param config.repeatY  Y方向贴图复制数
    * @param config.speedY  Y方向UV动画速度
    * @param config.tension   管道弯曲张力
    * @param config.closed   管道是否闭合
    * @param config.radius   管道半径
    * @param config.radialSegments   管道横截面的分段数目，默认值为8
    * @param config.offset 管道偏移量
    * @param config.name   管道名称
    * @param config.visible  管道默认显隐
    * @param config.skin   管道默认材质
    * @param config.color  管道颜色，传入该值会覆盖材质
    * @param config.beforeAdd   模型被加入到场景之前触发，传入false则不加入场景，返回模型对象
   */
  addTube (config) {
    if (config) {
      let {
        depthTest = true,
        depthWrite = true,
        points = [], attList = [], repeatX = 1, speedX = 0, repeatY = 1, speedY = 0, tension = 0, closed = false, radius = 8, radialSegments = 8, offset = { x: 0, y: 0, z: 0 }, name = '', visible = true, skin = "line.png", color = '', beforeAdd = () => { return true },
      } = config
      let nodes, curve, offsetNodes, tubeGeometry, material, texture, mesh
      nodes = points.length > 0 ? points : this.att2Points(attList)
      offsetNodes = nodes.map(item => {
        let point = new THREE.Vector3()
        point.x = item.x + offset.x
        point.y = item.y + offset.y
        point.z = item.z + offset.z
        return point
      })
      curve = new THREE.CatmullRomCurve3(offsetNodes, closed, 'catmullrom', tension)
      tubeGeometry = new THREE.TubeGeometry(curve, offsetNodes.length * 2, radius, radialSegments)

      material = new THREE.MeshBasicMaterial({
        transparent: true,
        side: THREE.DoubleSide,
        depthTest: depthTest,
        depthWrite: depthWrite
      })
      if (color) {
        let color2Obj = new THREE.Color(color)
        material.color = color2Obj
      } else {
        texture = new THREE.TextureLoader().load(IMGES_PATH + skin)
        texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
        texture.repeat.set(repeatX, repeatY)
        material.map = texture
      }
      mesh = new THREE.Mesh(tubeGeometry, material);
      mesh.name = name
      mesh.visible = visible
      if (speedX || speedY) {
        let animate = () => {
          if (texture) {
            texture.offset.x += speedX
            texture.offset.y += speedY
          }
          requestAnimationFrame(animate)
        }
        animate()
      }
      let result = beforeAdd(mesh)
      if (result) {
        this.otherGroup.add(mesh)
      }
    } else {
      throw '请传入addTube需要的配置'
    }
  }
  /**
   * 加载obj格式的模型
   * @param object.fileName 加载地址
   * @param object.name obj模型加入到场景时的名称
   * @param object.beforeAdd 加入场景之前触发，传入false则不加到场景
   */
  addObj (config) {
    if (config) {
      let { fileName, name = '', beforeAdd = () => { return false } } = config
      let loader = new OBJLoader()
      loader.load(OBJ_PATH + fileName + '.obj', obj => {
        obj.name = name
        let result = beforeAdd(obj)
        if (result) {
          this.otherGroup.add(obj)
        }
      }, (res) => {
        console.log('objLoad：' + (res.loaded / res.total * 100).toFixed(0) + ' %')
      })
    } else {
      throw '请传入addObj需要的参数'
    }
  }
  guiChanged (info, name = '', value = '') {
    if (!this.sky) {
      // console.log('未初始化sky')
      return
    }
    if (name) {
      info[name] = value
    }
    let sun = new THREE.Vector3();
    const uniforms = this.sky.material.uniforms;
    uniforms['turbidity'].value = info.turbidity;
    uniforms['rayleigh'].value = info.rayleigh;
    uniforms['mieCoefficient'].value = info.mieCoefficient;
    uniforms['mieDirectionalG'].value = info.mieDirectionalG;
    const phi = THREE.MathUtils.degToRad(90 - info.elevation);
    const theta = THREE.MathUtils.degToRad(info.azimuth);
    sun.setFromSphericalCoords(1, phi, theta);
    uniforms['sunPosition'].value.copy(sun);
    this.renderer.toneMappingExposure = info.exposure;
  }
  /**
   * 改变太阳角度
   * @params range 角度
   * 0°   日出
   * 90°  正午
   * 180° 日落
   * 270° 午夜
   * 360° 日出
   */
  setRange (range) {
    alert(range)
    let sunShine1 = this.scene.getObjectByName('sunShine')
    if (sunShine1) {
      // console.log(sunShine1)
      sunShine1.position.set(
        0,
        Math.sin(Math.PI / 180 * range) * this.sunRadius * 0.005,
        Math.cos(Math.PI / 180 * range) * this.sunRadius * -0.005,
      )
    }


    let bl = (Math.cos(Math.PI / 180 * (range - 90)) + 1) / 2
    let r = (((255 - 30) * bl) + 30).toFixed(0)
    let g = (((255 - 41) * bl) + 41).toFixed(0)
    let b = (((255 - 68) * bl) + 68).toFixed(0)
    let light = this.scene.getObjectByName('baseAmbientLight')
    light.color = new THREE.Color("rgb(" + r + "," + g + "," + b + ")")
    let fog = this.scene.fog
    if (range > -1 && range < 181) {
      // 白天
      fog.color = new THREE.Color("rgb(255,255,255)")
      this.sunRange = range
      let elevation = range
      const info = {
        turbidity: 5,
        rayleigh: 3,
        mieCoefficient: 0.07,
        mieDirectionalG: 1,
        elevation: 0,
        azimuth: 180,
        exposure: 1
      };
      this.guiChanged(info, 'elevation', elevation + '')
    } else {
      // 晚上
      fog.color = new THREE.Color("rgb(30,41,68)")
      let turbidity = 30 - (Math.abs(Math.cos(Math.PI / 180 * range)) * 10 + 10)
      let elevation = range - 180
      const info = {
        turbidity: 40,
        rayleigh: 0.1,
        mieCoefficient: 0.1,
        mieDirectionalG: 1,
        elevation: 0,
        azimuth: 180,
        exposure: 1
      };
      this.guiChanged(info, 'turbidity', turbidity + '')
      this.guiChanged(info, 'elevation', elevation + '')
    }
  }
  /**
   * 加入天空，配置待完成
   */
  addSky () {
    // Add Sky
    this.sky = new Sky();
    this.sky.scale.setScalar(this.sunRadius);
    this.scene.add(this.sky);
    /// GUI
    const info = {
      turbidity: 0,
      rayleigh: 3,
      mieCoefficient: 0,
      mieDirectionalG: 0,
      elevation: 0,
      azimuth: 180,
      exposure: 1
    };
    this.guiChanged(info);
    this.setRange(45);
  }
  /**
   * 增加poi标点
   * @param object.domList poi绑定的dom数组
   * @param object.infoList dom的数据
   * @param object.name 组名称
   */
  addCss3d (config) {
    if (config) {
      let { visible = false, domList, name, infoList, rotateX = 0, rotateY = 0, rotateZ = 0, scale = 1 } = config
      let group = new THREE.Group()
      group.name = name
      group.visible = visible
      group.scale.x = visible ? 1 : 0
      this.otherGroup.add(group)
      for (let i = 0; i < domList.length; i++) {
        const dom = domList[i];
        let label3d = new CSS3DObject(dom)
        let { x, y, z } = infoList[i].position
        label3d.position.set(x, y, z)
        label3d.scale.set(scale, scale, scale)
        label3d.rotateX(Math.PI / 180 * rotateX)
        label3d.rotateY(Math.PI / 180 * rotateY)
        label3d.rotateZ(Math.PI / 180 * rotateZ)
        group.add(label3d)
      }
      console.log(group)
      let render = () => {
        // label标签层的渲染器
        this.css3DRenderer.render(group, this.camera);
        requestAnimationFrame(render)
      }
      render()
    } else {
      throw '请传入add3Cssd需要的参数'
    }
  }
  /**
   * 相机飞行
   * @param object.duration 飞行时间
   * @param object.position 新的位置
   * @param object.controlsPosition 控制器新的中心点位置(围绕词典旋转等)
   */
  flyTo (object = {
    duration: 3000,
    position: { x: 100, y: 200, z: 300 },
    controlsPosition: { x: 0, y: 0, z: 0 }
  }) {
    let duration = object && object.duration ? object.duration : 5000
    let position = object && object.position ? object.position : { x: 100, y: 200, z: 300 }
    let controlsPosition = object && object.controlsPosition ? object.controlsPosition : { x: 0, y: 0, z: 0 }
    TWEEN.removeAll()
    const curPosition = this.camera.position
    const controlsTar = this.controls.target
    const tween = new TWEEN.Tween({
      x1: curPosition.x, // 相机当前位置x
      y1: curPosition.y, // 相机当前位置y
      z1: curPosition.z, // 相机当前位置z
      x2: controlsTar.x, // 控制当前的中心点x
      y2: controlsTar.y, // 控制当前的中心点y
      z2: controlsTar.z // 控制当前的中心点z
    }).to({
      x1: position.x, // 新的相机位置x
      y1: position.y, // 新的相机位置y
      z1: position.z, // 新的相机位置z
      x2: controlsPosition.x, // 新的控制中心点位置x
      y2: controlsPosition.y, // 新的控制中心点位置x
      z2: controlsPosition.z // 新的控制中心点位置x
    }, duration).easing(TWEEN.Easing.Cubic.InOut) // TWEEN.Easing.Cubic.InOut //匀速
    tween.onUpdate(() => {
      this.controls.enabled = false
      this.camera.position.set(tween._object.x1, tween._object.y1, tween._object.z1)
      this.controls.target.set(tween._object.x2, tween._object.y2, tween._object.z2)
      this.controls.update()
    })
    tween.onStart(() => {
      this.controls.enabled = false
    })
    tween.onComplete(() => {
      this.controls.enabled = true
    })
    tween.start()
    TWEEN.add(tween)
    const clock = new THREE.Clock()
    let animate = () => {
      requestAnimationFrame(animate)
      this.controls.update(clock.getDelta())
      TWEEN.update()
      // this.renderer.render(this.scene, this.camera)
    }
    animate()
  }
  /**
   * 替换模型对象的材质为水波材质
   * @param waterObject 需要替换的模型
   */
  addWater (waterObject) {
    if (waterObject) {
      let waterGeometry = waterObject.children[0].geometry.clone()
      let position = waterObject.position
      const textureLoader = new THREE.TextureLoader();
      const water = new Water(waterGeometry, {
        color: '#ffffff',
        flowDirection: new THREE.Vector2(0.7, 0.7),
        textureWidth: 512,
        textureHeight: 512,
        normalMap0: textureLoader.load('images/Water_1_M_Normal.jpg'),
        normalMap1: textureLoader.load('images/Water_2_M_Normal.jpg'),
        reflectivity: 0.1,
        flowSpeed: 0.3
      })
      water.position.x = position.x
      water.position.y = position.y + 1
      water.position.z = position.z
      water.rotation.x = Math.PI * -0.5
      this.otherGroup.add(water)
    } else {
      console.log('没有传入需要替换成水波材质的object')
    }
  }
  /** addGltf
   * 场景加入gltf文件
   * @param object.groupName 要加入的场景或组
   * @param object.fileName 模型文件名称
   * @param object.name 加入到场景或组时的命名
   * @param object.lngLat 模型经纬度定位
   * @param object.position 模型坐标定位
   * @param object.openShadow 模型是否打开阴影
   * @param object.beforeAdd 加入场景之前触发，传入false则不加到场景
   */
  addGltf (config) {
    if (config) {
      let { isGlow = true, wireframeConfig = {}, scale = 1, toWireframe = 'none', groupName = 'scene', fileName, name = '', lngLat = {}, position = { x: 0, y: 0, z: 0 }, openShadow = true, beforeAdd = () => { return true } } = config
      let gltfPosition = Object.prototype.hasOwnProperty.call(lngLat, "lng") ? this.lglt2xyz(lngLat) : position
      let { x, y, z } = gltfPosition
      let loader = new GLTFLoader()
      loader.load(GLB_PATH + fileName + '.glb', (glb) => {
        let { scene, animations } = glb
        scene.name = name
        scene.position.set(x, y, z)
        scene.scale.x = scene.scale.y = scene.scale.z = scale
        if (toWireframe === 'replace') {
          this.toWireframe({ isGlow, object3d: scene, opacity: wireframeConfig.opacity, color: wireframeConfig.color })
        } else if (toWireframe === 'copy') {
          let newObject3d = new THREE.Group()
          newObject3d.copy(scene)
          this[groupName].add(newObject3d)
          if (isGlow) {
            this.addGlow(newObject3d)
          }
          this.toWireframe({ isGlow, object3d: newObject3d, opacity: wireframeConfig.opacity, color: wireframeConfig.color })
        }
        if (openShadow) {
          this.openShadow(scene)
        }
        let result = beforeAdd(scene, animations)
        if (result) {
          this[groupName].add(scene)
        }
        if (isGlow) {
          this.addGlow(scene)
        }
      })

    } else {
      throw '请传入addGltf需要的参数'
    }
  }
  /**addFbx
   * 加载fbx格式模型
   * @param object.fileName 模型文件名称
   * @param object.groupName 要加入的组名称或场景
   * @param object.name 模型加入到场景时取名
   * @param object.openShadow 是否打开模型的阴影
   * @param object.lngLat 模型经纬度定位
   * @param object.position 模型坐标定位
   * @param object.beforeAdd 加入场景之前触发，传入false则不加到场景
   */
  addFbx (config) {
    if (config) {
      let { groupName = 'scene', fileName, name = '', openShadow = true, lngLat = {}, position = { x: 0, y: 0, z: 0 }, beforeAdd = () => { return true } } = config
      let fbxPosition = Object.prototype.hasOwnProperty.call(lngLat, "lng") ? this.lglt2xyz(lngLat) : position
      let { x, y, z } = fbxPosition
      let loader = new FBXLoader()
      loader.load(FBX_PATH + fileName + '.FBX', mesh => {
        mesh.position.set(x, y, z)
        mesh.name = name
        if (openShadow) {
          this.openShadow(mesh)
        }
        let result = beforeAdd(mesh)
        if (result) {
          this[groupName].add(mesh)
        }
      }, (res) => {
        console.log('load：' + (res.loaded / res.total * 100).toFixed(0) + ' %')
      }, (error) => {
        console.log(error)
      })
    } else {
      throw '请传入addGltf需要的参数'
    }
  }
  /**
   * 加入点光源
   * @param object.lng 光源所在经度
   * @param object.lat 光源所在纬度
   * @param object.position 光源所在坐标
   * @param object.color 光源颜色
   */
  addPointLight (object) {
    let { lng = -1, lat = -1, position = { x: 0, y: 0, z: 0 }, color = '#fff' } = object
    let xyz
    if (lng !== -1) {
      xyz = this.lngLatToXyz({ lng, lat })
    } else {
      xyz = position
    }
    const pointLight = new THREE.PointLight(color, 1, 50);
    pointLight.rotateX(Math.PI / 2)
    pointLight.rotateY(Math.PI / 2)
    pointLight.rotateZ(Math.PI / 2)
    pointLight.position.set(xyz.x, xyz.y, xyz.z)
    pointLight.castShadow = false;
    pointLight.shadow.mapSize.width = 180; // default
    pointLight.shadow.mapSize.height = 180; // default
    setTimeout(() => {
      this.scene.add(pointLight)
    }, 10000);
    // 辅助线
    const pointLightHelper = new THREE.PointLightHelper(pointLight);
    this.scene.add(pointLightHelper);
  }
}
